package com.lhq.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;


import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import static com.alibaba.fastjson.util.IOUtils.DIGITS;

/**
 * @ClassName AESUtils
 * @Description AES(对称加密)工具类
 * @Author amx
 * @Date 2022/1/12 9:05 下午
 **/
@Slf4j
public class AESUtil {


    /**
     * 算法/模式/补码方式
     */
    private final static  String ALGORITHM = "AES/CBC/PKCS5Padding";

    /**
     * 偏移量  must be 16 bytes long
     * 必须是16位
     */
    private final static String IV = "ti9v8iv.4039lhrh";

    /**
     * 秘钥 必须是16、24、32位16进制数字符串  不可随便修改 修改必须通知前端
     */
    private final static String key = "9d67bf7da7a87098gb0f0fae5e19906b";



    /**
     * 随机生成秘钥
     */
    public static void getKey() {
        try {
//            String seed = "fpwis";
//            SecureRandom rand = new SecureRandom(seed.getBytes());
            KeyGenerator kg = KeyGenerator.getInstance("AES");
            kg.init(128);
            //要生成多少位，只需要修改这里即可128, 192或256
            SecretKey sk = kg.generateKey();
            byte[] b = sk.getEncoded();
            String s = byteToHexString(b);
            System.out.println(new String(s));
            System.out.println(s);
            System.out.println("十六进制密钥长度为"+s.length());
            System.out.println("二进制密钥的长度为"+s.length()*4);
        }catch (Exception e) {
            log.error("随机生成秘钥异常",e);
            e.printStackTrace();
        }
    }


    /**
     * 使用指定的字符串生成秘钥
     */
    public static void getKeyByPass() {
        //生成秘钥
        String password="testkey";
        try {
            SecureRandom rand = new SecureRandom();
            KeyGenerator kg = KeyGenerator.getInstance("AES");
            // kg.init(128);//要生成多少位，只需要修改这里即可128, 192或256
            //SecureRandom是生成安全随机数序列，password.getBytes()是种子，只要种子相同，序列就一样，所以生成的秘钥就一样。
            kg.init(128, new SecureRandom(password.getBytes()));
            SecretKey sk = kg.generateKey();
            byte[] b = sk.getEncoded();
            String s = byteToHexString(b);
            System.out.println(s);
            System.out.println("十六进制密钥长度为"+s.length());
            System.out.println("二进制密钥的长度为"+s.length()*4);
        }
        catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            System.out.println("没有此算法。");
        }
    }

    /**
     * byte数组转化为16进制字符串
     * @param bytes
     * @return
     */
    public static String byteToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            String strHex=Integer.toHexString(bytes[i]);
            if(strHex.length() > 3) {
                sb.append(strHex.substring(6));
            } else {
                if(strHex.length() < 2) {
                    sb.append("0" + strHex);
                } else {
                    sb.append(strHex);
                }
            }
        }
        return sb.toString();
    }

    /*****************************************************
     * AES加密
     * @param content 加密内容
     * @param key 加密密码，由字母或数字组成
    此方法使用AES-128-CBC加密模式，key可以为16、24、32位
    加密解密key必须相同，如：abcd1234abcd1234
     * @return 加密密文
     ****************************************************/

    public static String enCode(String content, String key) {
        if (key == null || "".equals(key)) {
            log.info("key为空！");
            return null;
        }
        try {
            byte[] raw = key.getBytes();  //获得密码的字节数组
            SecretKeySpec skey = new SecretKeySpec(raw, "AES"); //根据密码生成AES密钥
            Cipher cipher = Cipher.getInstance(ALGORITHM);  //根据指定算法ALGORITHM自成密码器
            IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, skey,iv); //初始化密码器，第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作，第二个参数为生成的AES密钥
            byte [] byte_content = content.getBytes("utf-8"); //获取加密内容的字节数组(设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte [] encode_content = cipher.doFinal(byte_content); //密码器加密数据
            return Base64.encodeBase64String(encode_content); //将加密后的数据转换为字符串返回
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 加密字符串
     * @param content  待加密内容
     * 此方法使用AES-128-CBC加密模式，key可以为16、24、32位
     * 加密解密key必须相同，如：abcd1234abcd1234
     * @return
     */
    public static String enCodeString(String content) {
        return enCode(content,key);
    }

    /*****************************************************
     * AES解密
     * @param content 加密密文
     * @param key 加密密码,由字母或数字组成
    此方法使用AES-128-CBC加密模式，key可以为16、24、32位
    加密解密key必须相同
     * @return 解密明文
     ****************************************************/

    public static String deCode(String content, String key) {
        if (key == null || "".equals(key)) {
            log.info("key为空！");
            return null;
        }
        try {
            byte[] raw = key.getBytes("UTF-8");  //获得密码的字节数组
            SecretKeySpec skey = new SecretKeySpec(raw, "AES"); //根据密码生成AES密钥
            Cipher cipher = Cipher.getInstance(ALGORITHM);  //根据指定算法ALGORITHM自成密码器
            IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skey, iv); //初始化密码器，第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作，第二个参数为生成的AES密钥
            byte [] encode_content = Base64.decodeBase64(content); //把密文字符串转回密文字节数组
            byte [] byte_content = cipher.doFinal(encode_content); //密码器解密数据
            return new String(byte_content,"utf-8"); //将解密后的数据转换为字符串返回

        } catch (Exception e) {
            log.error("解码出错：",e);
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密加密密文
     * @param content 加密密文
     * 此方法使用AES-128-CBC加密模式，key可以为16、24、32位
     * 加密解密key必须相同
     * @return
     */
    public static String deCodeString(String content) {
        return deCode(content,key);
    }


    /**
     * base64编码格式String转hexString
     *@param base64String
     * @return
     * @throws IOException
     */
    public static final String base64StringToHexString(String base64String) throws IOException {
        BASE64Decoder decoder = new BASE64Decoder();
        byte[] decoded = decoder.decodeBuffer(base64String);
        final StringBuffer hex = new StringBuffer(decoded.length * 2);
        for (int i = 0; i < decoded.length; i++) {
            hex.append(DIGITS[(decoded[i] >>> 4) & 0x0F]);
            hex.append(DIGITS[decoded[i] & 0x0F]);
        }
        return hex.toString();
    }


    /**
     * hexString转base64编码格式String
     * @param hexString
     * @return
     * @throws IOException
     */
    public static final String hexStringToBase64String(String hexString) throws IOException {
        int m = 0, n = 0;
        int byteLen = hexString.length() / 2; // 每两个字符描述一个字节
        byte[] ret = new byte[byteLen];
        for (int i = 0; i < byteLen; i++) {
            m = i * 2 + 1;
            n = m + 1;
            int intVal = Integer.decode("0x" + hexString.substring(i * 2, m) + hexString.substring(m, n));
            ret[i] = Byte.valueOf((byte)intVal);
        }
        BASE64Encoder encoder = new BASE64Encoder();
        String base64String = encoder.encode(ret);
        return base64String;
    }

    /**
     * 前端传输过来的hexString解密
     * @param content
     * @return
     */
    public static String hexStringDeCode(String content) {
        if (key == null || "".equals(key)) {
            log.info("key为空！");
            return null;
        }
        try {
            //现将前端传过来的hexString转为Base64String
            content = hexStringToBase64String(content);

            byte[] raw = key.getBytes("UTF-8");  //获得密码的字节数组
            SecretKeySpec skey = new SecretKeySpec(raw, "AES"); //根据密码生成AES密钥
            Cipher cipher = Cipher.getInstance(ALGORITHM);  //根据指定算法ALGORITHM自成密码器
            IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skey, iv); //初始化密码器，第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作，第二个参数为生成的AES密钥
            byte [] encode_content = Base64.decodeBase64(content); //把密文字符串转回密文字节数组
            byte [] byte_content = cipher.doFinal(encode_content); //密码器解密数据
            return new String(byte_content,"utf-8"); //将解密后的数据转换为字符串返回

        } catch (Exception e) {
            log.error("解码出错：",e);
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 前端的加密方式
     * @param content
     * @return
     */
    public static String enCodeToHexString(String content) {
        if (key == null || "".equals(key)) {
            log.info("key为空！");
            return null;
        }
        try {
            byte[] raw = key.getBytes();  //获得密码的字节数组
            SecretKeySpec skey = new SecretKeySpec(raw, "AES"); //根据密码生成AES密钥
            Cipher cipher = Cipher.getInstance(ALGORITHM);  //根据指定算法ALGORITHM自成密码器
            IvParameterSpec iv = new IvParameterSpec(IV.getBytes());
            cipher.init(Cipher.ENCRYPT_MODE, skey,iv); //初始化密码器，第一个参数为加密(ENCRYPT_MODE)或者解密(DECRYPT_MODE)操作，第二个参数为生成的AES密钥
            byte [] byte_content = content.getBytes("utf-8"); //获取加密内容的字节数组(设置为utf-8)不然内容中如果有中文和英文混合中文就会解密为乱码
            byte [] encode_content = cipher.doFinal(byte_content); //密码器加密数据
            return base64StringToHexString(Base64.encodeBase64String(encode_content)); //将加密后的数据转换为字符串返回
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) throws IOException {


        // fp_ocr 加解密测试
       System.out.println(enCodeString("机组按月度晚高峰期间（16:15-21:00）"));
       System.out.println(deCodeString("mtDZUnpuNWYI3yyCvQfO2w=="));
    }
}
